package com.cskaoyan.javase.oop3._2inner._5lambda;

/**
 * @description: lambda表达式的简化
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

import java.util.Arrays;

/**
 * lambda表达式的简化的原则:
 *      1.简化后不能有歧义
 *      2.lambda表达式能够简化的依据是功能接口中仅有一个必须要实现的抽象方法
 *
 * lambda表达式的语法:
 *      (形参列表) -> { //方法体 }
 * 简化:
 *      1.(形参列表)
 *          能不能简化呢?
 *          形参列表中数据类型是不需要写出来的,因为功能接口中抽象方法只有一个
 *          特殊的,如果形参列表中仅有一个参数,那么括号也是可以省略的
 *          当然如果没有参数,空参,需要写()即可
 *      2.{
 *          //方法体
 *        }
 *       也是可以简化的
 *       当方法体仅有一条语句时,那么{}可以省略
 *       特殊的,当方法的方法体中仅有一条返回值语句时,可以连带{}和return一起省略
 *
 *     3. ->
 *     作为lambda表达式的运算符,多数时候都是不能省略的,特殊情况下也是可以的
 *     lambda表达式不仅可以自己重写方法体,更重要的是可以把方法的实现直接指向一个已经存在的方法
 *     语法:
 *          (形参列表) -> 已经存在的方法()
 * 指向的时候直接写方法名即可(前提在同一个类中)
 *
 * 已存在的方法如何和接口中的抽象方法匹配?
 *      必须要求形参列表一样,返回值类型也是一样的,其余的没有影响
 *
 * 进一步简化,去掉"(形参列表) ->",直接"="指向一个已实现的方法,这在lambda表达式当中称为"方法引用"
 * 语法:
 *      接口名 对象名 = 方法的归属者::方法名;
 * 谁是方法的归属者?
 *      1.成员方法,属于对象
 *      2.静态方法,属于类
 * 以上lambda表达式的简化
 *
 *
 * lambda表达式的作用域问题
 * lambda表达式对象没有自己单独的作用域，和方法共享作用域
 *
 *
 */
public class Demo2 {
    public static void main(String[] args) {
        //int a = 10;
        //Variable 'a' is already defined in the scope
        INoReturnTwoParam ip = (a, b) -> {
            System.out.println("hell " + a + "  hello " + b);
        };
        ip.test(100, 200);

        INoReturnOneParam ip1 = a -> {
            System.out.println("hell " + a);
        };
        ip1.test(100);

        INoReturnNoParam ip2 = () -> {
            System.out.println("hello world!");
        };
        ip2.test();

        INoReturnNoParam ip3 = () -> System.out.println("我省略了大括号!");
        ip3.test();

        IHasReturnNoParam ip4 = () -> 10;
        System.out.println(ip4.test());

        INoReturnNoParam ip5 = () -> myTest();
        ip5.test();

        INoReturnNoParam ip6 = Demo2::myTest; //方法引用
        /**
         * lambda表达式实现接口中抽象方法时,有时候必不可少会写很多行代码
         * 这时候就可以使用方法引用,指向已实现的方法
         * 已实现的方法不仅包括自定义的,还包括所有的源码,比如jdk中的
         */
        ip6.test();

        //使用方法引用,指向已实现的printLn(int a)方法
        INoReturnOneParam ip7 = System.out::println;
        ip7.test(10);

        //创建对象数组
        Teacher[] teas = new Teacher[3];
        Teacher t1 = new Teacher(18, "张三");
        Teacher t2 = new Teacher(88, "李四");
        Teacher t3 = new Teacher(20, "王二");
        teas[0] = t1;
        teas[1] = t2;
        teas[2] = t3;

        //完成lambda表达式方法引用

        Demo2 d = new Demo2();
        Traverse traverse = d::traverseTeacherArr;
        traverse.traverseTeacher(teas);
    }

    public static void myTest() {
        System.out.println("我是已经实现的无参无返回值方法");
    }

    //写一个方法完成对某个对象数组的遍历
    public void traverseTeacherArr(Teacher[] teas) {
        for (Teacher t : teas) {
            t.print();
        }
    }

}

@FunctionalInterface
interface Traverse{
    void traverseTeacher(Teacher[] teas);
}

class Teacher {
    int age;
    String name;

    public Teacher() {
    }

    public Teacher(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public void print() {
        System.out.println("姓名为:" + name + "\t年龄为" + age);
    }
}


//无返回值无参数的功能接口
@FunctionalInterface
interface INoReturnNoParam {
    void test();
}

//无返回值有一个参数的功能接口
@FunctionalInterface
interface INoReturnOneParam {
    void test(int a);
}

//无返回值两个参数的功能接口
@FunctionalInterface
interface INoReturnTwoParam {
    void test(int a, int b);
}

//有返回值无参数的功能接口
@FunctionalInterface
interface IHasReturnNoParam {
    int test();
}

//有返回值一个参数的功能接口
@FunctionalInterface
interface IHasReturnOneParam {
    int method(int a);
}

//有返回值两个参数的功能接口
@FunctionalInterface
interface IHasReturnTwoParam {
    int test(int a, int b);
}